/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fri.cbw.deterministic.tool;

import fri.cbw.GenericTool.AbstractGenericTool;
import fri.cbw.GenericTool.ToolTopComponent;
import fri.cbw.GenericTool.ToolWrapper;
import fri.cbw.deterministic.tool.Bundle;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Collection;
import java.util.Map;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableModel;
import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
import org.apache.commons.math3.ode.ParameterizedODE;
import org.apache.commons.math3.ode.UnknownParameterException;
import org.netbeans.api.visual.graph.GraphScene;
import org.netbeans.api.visual.widget.general.IconNodeWidget;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.ActionID;
import org.openide.util.Exceptions;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;

/**
 * Top component which displays something.
 */
@TopComponent.Description(
    preferredID = "EulerDeterministicEngineTopComponent",
//iconBase="SET/PATH/TO/ICON/HERE", 
persistenceType = TopComponent.PERSISTENCE_NEVER)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
@ActionID(category = "Window", id = "fri.cbw.deterministic.EulerDeterministicEngineTopComponent")
@Messages({
    "CTL_EulerDeterministicEngineAction=EulerDeterministicEngine",
    "CTL_EulerDeterministicEngineTopComponent=EulerDeterministicEngine Window",
    "HINT_EulerDeterministicEngineTopComponent=This is a EulerDeterministicEngine window"
})
public final class EulerDeterministicEngineTopComponent extends ToolTopComponent {

    public EulerDeterministicEngineTopComponent(AbstractGenericTool gt) {
        super(gt);
        initComponents();
        setName(Bundle.CTL_EulerDeterministicEngineTopComponent());
        setToolTipText(Bundle.HINT_EulerDeterministicEngineTopComponent());
        
        //parametersTable.getColumnModel().getColumn(2).setCellRenderer(new AllDecimalTableCellRenderer());
        //initialValuesTable.getColumnModel().getColumn(2).setCellRenderer(new AllDecimalTableCellRenderer());

    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        jFrame1 = new javax.swing.JFrame();
        jPanel4 = new javax.swing.JPanel();
        jPanel1 = new javax.swing.JPanel();
        jButton3 = new javax.swing.JButton();
        jButton1 = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jPanel5 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        labelConValid = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        labelNoE = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        textFdT = new javax.swing.JFormattedTextField();
        textFSimTime = new javax.swing.JFormattedTextField();
        textSampleCount = new javax.swing.JFormattedTextField();
        jSeparator1 = new javax.swing.JSeparator();
        jSeparator2 = new javax.swing.JSeparator();
        jProgressBar1 = new javax.swing.JProgressBar();
        jScrollPane3 = new javax.swing.JScrollPane();
        parametersTable = new javax.swing.JTable();
        jScrollPane1 = new javax.swing.JScrollPane();
        initialValuesTable = new javax.swing.JTable();

        javax.swing.GroupLayout jFrame1Layout = new javax.swing.GroupLayout(jFrame1.getContentPane());
        jFrame1.getContentPane().setLayout(jFrame1Layout);
        jFrame1Layout.setHorizontalGroup(
            jFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        jFrame1Layout.setVerticalGroup(
            jFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 100, Short.MAX_VALUE)
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 100, Short.MAX_VALUE)
        );

        setLayout(new java.awt.BorderLayout());

        jPanel1.setLayout(new java.awt.GridLayout(1, 0));

        org.openide.awt.Mnemonics.setLocalizedText(jButton3, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jButton3.text")); // NOI18N
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });
        jPanel1.add(jButton3);

        org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jButton1.text_2")); // NOI18N
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        jPanel1.add(jButton1);

        add(jPanel1, java.awt.BorderLayout.PAGE_END);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        java.awt.GridBagLayout jPanel5Layout = new java.awt.GridBagLayout();
        jPanel5Layout.columnWidths = new int[] {0, 5, 0};
        jPanel5Layout.rowHeights = new int[] {0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0};
        jPanel5.setLayout(jPanel5Layout);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jLabel1.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 6;
        jPanel5.add(jLabel1, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jLabel2.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 8;
        jPanel5.add(jLabel2, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jLabel3.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        jPanel5.add(jLabel3, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(labelConValid, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.labelConValid.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        jPanel5.add(labelConValid, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jLabel5.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        jPanel5.add(jLabel5, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(labelNoE, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.labelNoE.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        jPanel5.add(labelNoE, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.jLabel4.text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 10;
        jPanel5.add(jLabel4, gridBagConstraints);

        textFdT.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat(""))));
        textFdT.setText(org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.textFdT.text")); // NOI18N
        textFdT.setPreferredSize(new java.awt.Dimension(100, 20));
        textFdT.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                textFdTPropertyChange(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 6;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(textFdT, gridBagConstraints);

        textFSimTime.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat(""))));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 8;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(textFSimTime, gridBagConstraints);

        textSampleCount.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(java.text.NumberFormat.getIntegerInstance())));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 10;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(textSampleCount, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(jSeparator1, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 12;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(jSeparator2, gridBagConstraints);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 14;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        jPanel5.add(jProgressBar1, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.ipadx = 225;
        gridBagConstraints.ipady = 155;
        jPanel2.add(jPanel5, gridBagConstraints);

        parametersTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null}
            },
            new String [] {
                "Parameter name", "Parameter value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.Double.class
            };
            boolean[] canEdit = new boolean [] {
                false, true
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        parametersTable.getTableHeader().setReorderingAllowed(false);
        jScrollPane3.setViewportView(parametersTable);
        if (parametersTable.getColumnModel().getColumnCount() > 0) {
            parametersTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.parametersTable.columnModel.title0")); // NOI18N
            parametersTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.parametersTable.columnModel.title1")); // NOI18N
        }

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.ipadx = 429;
        gridBagConstraints.ipady = 248;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel2.add(jScrollPane3, gridBagConstraints);

        initialValuesTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null}
            },
            new String [] {
                "Variable name", "Initial Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.Double.class
            };
            boolean[] canEdit = new boolean [] {
                false, true
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        initialValuesTable.getTableHeader().setReorderingAllowed(false);
        jScrollPane1.setViewportView(initialValuesTable);
        if (initialValuesTable.getColumnModel().getColumnCount() > 0) {
            initialValuesTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.initialValuesTable.columnModel.title0")); // NOI18N
            initialValuesTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(EulerDeterministicEngineTopComponent.class, "EulerDeterministicEngineTopComponent.initialValuesTable.columnModel.title1")); // NOI18N
        }

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.ipadx = 429;
        gridBagConstraints.ipady = 248;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel2.add(jScrollPane1, gridBagConstraints);

        add(jPanel2, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private EulerDeterministicEngine getEngine(){
        ToolWrapper tw=(ToolWrapper) this.getToolWrapper();
        
        AbstractGenericTool abTool=tw.getNodeGenericTool();
        
        if(!(abTool instanceof EulerDeterministicEngine)) {
            throw new IllegalArgumentException("Associated engine must be of EulerDeterministicEngine type");
        }
        return (EulerDeterministicEngine) abTool;
    }
    
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        jProgressBar1.setIndeterminate(true);
        final ToolWrapper tw=(ToolWrapper) this.getToolWrapper();
        final EulerDeterministicEngine engine=getEngine();
        try {
            
            
            NumberFormat numberFormat = NumberFormat.getNumberInstance();
            
            String dTString=textFdT.getText();
            double dT=numberFormat.parse(dTString).doubleValue();
            engine.setDeltaT(dT);
            
            String simTimeString=textFSimTime.getText();
            int simTime=numberFormat.parse(simTimeString).intValue();
            engine.setSimulationTime(simTime);
            
            String numOfSamplesString=textSampleCount.getText();
            int numOfSamples=numberFormat.parse(numOfSamplesString).intValue();
            engine.setNumberOfSamples(numOfSamples);
            
            updateInitialValuesFromTable(engine);
            updateParametersFromTable(engine);
            
            
        } catch (ParseException ex) {
            Exceptions.printStackTrace(ex);
            DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message("Numbers parsing failed."));
        }
        

        SwingWorker sw = new SwingWorker() {
            @Override
            protected Object doInBackground() throws Exception {
                engine.calculate();
                jProgressBar1.setIndeterminate(false);
                return null;
            }
        };
        sw.execute();
    }//GEN-LAST:event_jButton1ActionPerformed

    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
        EulerDeterministicEngine engine=getEngine();
        
        engine.updateFromModel();
        
        NumberFormat numberFormat = NumberFormat.getNumberInstance();
        
        labelNoE.setText(numberFormat.format(engine.getNumberOfEquations()));
        labelConValid.setText(Boolean.toString(engine.isConnectionsValid()));
        
        textFdT.setText(numberFormat.format(engine.getDeltaT()));
        textFSimTime.setText(numberFormat.format(engine.getSimulationTime()));
        textSampleCount.setText(numberFormat.format(engine.getNumberOfSamples()));
        
        updateParametersFromEngine(engine);
        updateInitialValuesFromEngine(engine);
        
    }//GEN-LAST:event_jButton3ActionPerformed

    private void textFdTPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_textFdTPropertyChange
        // TODO add your handling code here:
    }//GEN-LAST:event_textFdTPropertyChange

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTable initialValuesTable;
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton3;
    private javax.swing.JFrame jFrame1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JProgressBar jProgressBar1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JLabel labelConValid;
    private javax.swing.JLabel labelNoE;
    private javax.swing.JTable parametersTable;
    private javax.swing.JFormattedTextField textFSimTime;
    private javax.swing.JFormattedTextField textFdT;
    private javax.swing.JFormattedTextField textSampleCount;
    // End of variables declaration//GEN-END:variables
    @Override
    public void componentOpened() {
        jButton3ActionPerformed(null);
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    private void updateInitialValuesFromEngine(EulerDeterministicEngine engine) {
        Map<String, Double> initialValues = engine.getInitialValues();
        DefaultTableModel initialValuesTableModel=(DefaultTableModel) initialValuesTable.getModel();
        
        initialValuesTableModel.setRowCount(0);
        
        for(String key : initialValues.keySet()){
            initialValuesTableModel.addRow(new Object[]{key, initialValues.get(key)});
        }
    }
    
    private void updateInitialValuesFromTable(EulerDeterministicEngine engine){
        Map<String, Double> initialValues = engine.getInitialValues();
        DefaultTableModel initialValuesTableModel=(DefaultTableModel) initialValuesTable.getModel();
        
        for (int i = 0; i < initialValuesTableModel.getRowCount(); i++) {
            String variableName=(String)initialValuesTableModel.getValueAt(i, 0);
            double variableValue=(Double)initialValuesTableModel.getValueAt(i, 1);
            initialValues.put(variableName, variableValue);
        }
    }
    
    private void updateParametersFromEngine(EulerDeterministicEngine engine) throws UnknownParameterException {
        FirstOrderDifferentialEquations ode = engine.getOde();
        
        if(ode instanceof ParameterizedODE){
            ParameterizedODE pOde=(ParameterizedODE) ode;
            Collection<String> parameterNames = pOde.getParametersNames();
            DefaultTableModel tableModel=(DefaultTableModel) parametersTable.getModel();
            
            tableModel.setRowCount(0);
            
            for(String parameterName : parameterNames){
                double parameterValue=pOde.getParameter(parameterName);
                tableModel.addRow(new Object[]{parameterName, parameterValue});
            }
        }
        else{
            DefaultTableModel tableModel=(DefaultTableModel) parametersTable.getModel();
            tableModel.setRowCount(0);
            parametersTable.setVisible(false);
        }
    }
    
    private void updateParametersFromTable(EulerDeterministicEngine engine){
        FirstOrderDifferentialEquations ode = engine.getOde();
        
        if(ode instanceof ParameterizedODE){
            ParameterizedODE pOde=(ParameterizedODE) ode;

            DefaultTableModel tableModel=(DefaultTableModel) parametersTable.getModel();
            
            for (int i = 0; i < tableModel.getRowCount(); i++) {
                String variableName=(String)tableModel.getValueAt(i, 0);
                double variableValue=(Double)tableModel.getValueAt(i, 1);
                pOde.setParameter(variableName, variableValue);
            }
        }
    }

    @Override
    public void doSave() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
    
}
